/*
    Copyright 2006 Andrew Wilkinson <andrew@indiegigs.co.uk>

    This file is part of libgp2x.

    libgp2x is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    libgp2x is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with libgp2x; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "stddef.h"

#ifndef __LIB_GP2X_H__
#define __LIB_GP2X_H__

#define LIBGP2x_VERSION "0.0.1"
#define LIBGP2x_COPYRIGHT "Copyright 2006 Andrew Wilkinson <andrew@indiegigs.co.uk>"
#define LIBGP2x_LICENSE "Libgp2x is licensed under the GNU GPL. See the file LICENSE for more details"

#ifndef HOST_PC
#define GP2x_940_OFFSET 0x2000000
#else
extern unsigned long GP2x_940_OFFSET;
#endif

#define GP2x_INIT_VIDEO 1
#define GP2x_INIT_INPUT 2
#define GP2x_INIT_MESSAGE 4
#define GP2x_INIT_ALL (GP2x_INIT_VIDEO | GP2x_INIT_INPUT | GP2x_INIT_MESSAGE)

/** \ingroup core
  * \brief This function initalizes the GP2x.
  *
  * This function must be called before any other GP2x_* function is called. If it is not then the results are undefined.
  */
void GP2x_init(int init);

/** \ingroup core
  * \brief This function cleans up any memory structures used by libgp2x.
  *
  * This function should always be called at the end of the program therefore you should consider using atexit
  * to ensure this happens.
  * \code
  * atexit(GP2x_finalize);
  * \endcode
  */
void GP2x_finalize();

/** \ingroup graphics
  * \brief This functions causes the graphics drawn to the screen since the previous call to be displayed.
  *
  * This function is syncronous and therefore there is no reason to call GP2x_video_vsync seperately.
  */
void GP2x_flip();

/** \ingroup graphics
  * \brief This functions causes the graphics drawn to the screen since the previous call to be displayed.
  *
  * This function does not wait for a vertical blank and therefore can result in tearing.
  * Consider using GP2x_flip() unless you <b>really</b> want this.
  */
void GP2x_flip_unsync();

/** \ingroup graphics
  * \brief This function blocks until a horizontal sync event occurs.
  *
  * A horizontal sync event occurs at the when each line of the display has been finished drawing.
  * The event lasts for a very short period of time, and there is no way to know which line has just been drawn.
  */
void GP2x_video_hsync();

/** \ingroup graphics
  * \brief This function blocks until a vertical blank event occurs.
  *
  * A vertical blank occurs at the end of drawing each frame of the display. This is usually 60 times every second.
  */
void GP2x_video_vsync();

#ifndef HOST_PC
#ifdef GP2x_940
#define GP2x_upper_memory 0x0
#define GP2x_convert_ptr_to(x) (x)
#define GP2x_convert_ptr_from(x) (x)
#define GP2x_convert_ptr_to2(x) (x)
#define GP2x_convert_ptr_from2(x) (x)
#else
extern volatile unsigned char* GP2x_upper_memory;
#define GP2x_convert_ptr_to(x) ((x) == 0 ? (void*)0 : ((void*)((unsigned long)x - (unsigned long)GP2x_upper_memory)))
#define GP2x_convert_ptr_from(x) ((x) == 0 ? (void*)0 : ((void*)((unsigned long)x + (unsigned long)GP2x_upper_memory)))
#define GP2x_convert_ptr_to2(x) ((void*)((unsigned long)x - (unsigned long)GP2x_upper_memory))
#define GP2x_convert_ptr_from2(x) ((void*)((unsigned long)x + (unsigned long)GP2x_upper_memory))
#endif
#else
extern volatile unsigned char* GP2x_upper_memory;
#define GP2x_convert_ptr_to(x) ((x) == 0 ? (void*)0 : ((void*)((unsigned long)x - (unsigned long)GP2x_upper_memory)))
#define GP2x_convert_ptr_from(x) ((x) == 0 ? (void*)0 : ((void*)((unsigned long)x + (unsigned long)GP2x_upper_memory)))
#define GP2x_convert_ptr_to2(x) ((void*)((unsigned long)x - (unsigned long)GP2x_upper_memory))
#define GP2x_convert_ptr_from2(x) ((void*)((unsigned long)x + (unsigned long)GP2x_upper_memory))
#endif

#ifndef GP2x_940t
/** \ingroup memory
  * \brief This function returns memory from the upper portion of the GP2x's memory.
  * Only available on the 920t.
  */
void* GP2x_upper_memory_malloc(size_t size);

/** \ingroup memory
  * \brief Changes the size of the memory pointed to by ptr to size bytes
  * If size is NULL this is equivalent to calling GP2x_upper_memory_free(ptr).
  * If ptr is NULL this is equivalent to calling GP2x_upper_memory_malloc(ptr).
  * Only available on the 920t.
  */
void* GP2x_upper_memory_realloc(void* ptr, size_t size);

/** \ingroup memory
  * \brief This function frees memory previously allocated by GP2x_upper_memory_malloc()
  * Only available on the 920t.
  */
void GP2x_upper_memory_free(void* ptr);

/** \ingroup memory
  * \brief Fills in the parameters with current status of the upper memory.
  * \internal
  * Only available on the 920t.
  */
void GP2x_upper_memory_count(unsigned int* usedmemory, unsigned int* usedblocks, unsigned int* freeblocks);
#endif

/** \ingroup input
  * \brief Joystick pressed upwards
  */
#define JOYSTICK_UP        (1<<0)
/** \ingroup input
  * \brief Joystick pressed up and left
  */
#define JOYSTICK_UPLEFT    (1<<1)
/** \ingroup input
  * \brief Joystick pressed left
  */
#define JOYSTICK_LEFT      (1<<2)
/** \ingroup input
  * \brief Joystick pressed down and left
  */
#define JOYSTICK_DOWNLEFT  (1<<3)
/** \ingroup input
  * \brief Joystick pressed downwards
  */
#define JOYSTICK_DOWN      (1<<4)
/** \ingroup input
  * \brief Joystick pressed down and right
  */
#define JOYSTICK_DOWNRIGHT (1<<5)
/** \ingroup input
  * \brief Joystick pressed right
  */
#define JOYSTICK_RIGHT     (1<<6)
/** \ingroup input
  * \brief Joystick pressed up and right
  */
#define JOYSTICK_UPRIGHT   (1<<7)
/** \ingroup input
  * \brief Joystick pressed in
  */
#define JOYSTICK_PUSH      (1<<8)
/** \ingroup input
  * \brief Button A pressed
  */
#define BUTTON_A           (1<<9)
/** \ingroup input
  * \brief Button B pressed
  */
#define BUTTON_B           (1<<10)
/** \ingroup input
  * \brief Button X pressed
  */
#define BUTTON_X           (1<<11)
/** \ingroup input
  * \brief Button Y pressed
  */
#define BUTTON_Y           (1<<12)
/** \ingroup input
  * \brief The left trigger pressed
  */
#define TRIGGER_LEFT       (1<<13)
/** \ingroup input
  * \brief The right trigger pressed
  */
#define TRIGGER_RIGHT      (1<<14)
/** \ingroup input
  * \brief The volume up button pressed
  */
#define VOLUME_UP          (1<<15)
/** \ingroup input
  * \brief The volume down button pressed
  */
#define VOLUME_DOWN        (1<<16)
/** \ingroup input
  * \brief The Start button
  */
#define START              (1<<17)
/** \ingroup input
  * \brief The select button
  */
#define SELECT             (1<<18)

/** \ingroup input
  * \brief Returns an integer showing which buttons/joystick directions are currently being pressed.
  */
unsigned int GP2x_input_poll();

/** \ingroup input
  * \brief Returns true if the given buttons/joystick directions are currently being pressed.
  */
#define GP2x_input_pressed(input) (GP2x_input_poll() & input)

#ifndef GP2x_940
/** \ingroup dualcore
  * \brief Loads the file given by filename onto the 940t and executes it.
  * Only available on the 920t.
  */
void GP2x_940_load(char* filename);

/** \ingroup dualcore
  * \brief Pauses the 940t processor
  * Only available on the 920t.
  */
void GP2x_940_pause();

/** \ingroup dualcore
  * \brief Unpauses the 940t.
  * Only available on the 920t.
  */
void GP2x_940_unpause();

/** \ingroup dualcore
  * \brief Enables the 940t.
  * Only available on the 920t.
  */
void GP2x_940_enable();

/** \ingroup dualcore
  * \brief Places the 940t into reset mode.
  * Only available on the 920t.
  */
void GP2x_940_reset();
#endif

/** \ingroup dualcore
  * \brief Waits until the other processor has processed all messages.
  * This functions requires that the other processor calls either GP2x_poll or GP2x_wait_message or it will not return.
  */
void GP2x_sync();

/** \ingroup dualcore
  * \brief Places a message into the message queue of the other processor.
  * If await return is true then the function will block until the message is processed and will return the appropriate value. 
  * \param message The message to send
  * \param value The value to send with the message
  * \param await_return A true value will make the function block, false and it will return as soon as the message is posted.
  * \return The value returned by the other processor if await_return was true, otherwise undefined.
  */
long GP2x_postMessage(unsigned long message, unsigned long value, unsigned char await_return);

/** \ingroup dualcore
  * \brief Checks the message queue to see if there is a message.
  *
  * Although this function may return false, indicating there is no message to process the two variables passed in may still
  * have been overwritten as it they may have been used to process an internal message.
  *
  * Do no assume that values in the parameters will remain intact after a call to this function.
  *
  * \param message A pointer to a variable which will hold the message id.
  * \param value A pointer to a variable which will hold the value sent with the message.
  * \return True if the variables have been filled with a valid message, false if not.
  */
unsigned char GP2x_poll(unsigned long* message, unsigned long* value);

/** \ingroup dualcore
  * \brief Blocks until a message is placed into the message queue.
  *
  * \param message A pointer to a variable which will hold the message id.
  * \param value A pointer to a variable which will hold the value sent with the message.
  */
void GP2x_wait_message(unsigned long* message, unsigned long* value);

/** \ingroup dualcore
  * \brief Returns a value to the other processor.
  *
  * If a message has a return value this will send it to the other processor.
  *
  * \param value The value to send to the other processor.
  */
void GP2x_return(unsigned long value);

/** \ingroup graphics
  * \internal
  * \brief This points to the back buffer where current drawing should point.
  *
  * Although accessible to user programs this pointer should not be directly accessed, modified or stored.
  */
extern unsigned short* GP2x_screen;

/** \ingroup graphics
  * \brief This function returns a unsigned short suitable for assigning to the screen.
  * \param r The amount of red (0 - 255)
  * \param g The amount of green (0 - 255)
  * \param b The amount of blue (0 - 255)
  * \param a The alpha component (0 - 255)
  *
  * Currently the alpha component is ignored.
  */
//define GP2x_getColour15(r, g, b, a) ((((r) & 0xF8) << 8) | (((g) & 0xF8) << 3) | (((b) & 0xF8) >> 3) | ((a) << 5))
#define GP2x_getColour15(r, g, b, a) ((((r) & 0xF8) << 8) | (((g) & 0xFC) << 3) | (((b) & 0xF8) >> 3))

/** \ingroup graphics
  * \brief This function places the given colour at the appropriate point on the screen.
  * \param x The x coordinate (0 - 320)
  * \param y The y coordinate (0 - 240)
  * \param c The colour (unsigned short)
  *
  * This function performs no error checking. If the x or y coordinate does not fall within the correct bounds
  * then the results are undefined.
  */
#define GP2x_putColour(x, y, c) GP2x_screen[(y)*320+(x)] = (unsigned short)(c)

/** \ingroup graphics
  * \brief This function places the given colour at the appropriate point on the screen.
  * \param x The x coordinate (0 - 320)
  * \param y The y coordinate (0 - 240)
  * \param r The amount of red (0 - 255)
  * \param g The amount of greeb (0 - 255)
  * \param b The amount of blue (0 - 255)
  * \param a The alpha component (0 - 255)
  *
  * This function performs no error checking. If the x or y coordinate does not fall within the correct bounds
  * then the results are undefined.
  *
  * Currently the alpha component is ignored.
  */
#define GP2x_putPixel(x, y, r, g, b, a) GP2x_putColour(x, y, GP2x_getColour15(r, g, b, a))

enum GP2x_options {
    GP2x_940_CONTROL_SCREEN
};

void GP2x_set_option(enum GP2x_options option);

#endif
